#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unordered_map>
#include "mf.cpp"

#define OP_CODE(order) order & 0xff000000 >> 24;

#define ABC_P1(order) order & 0x00ff0000 >> 16;
#define ABC_P2(order) order & 0x0000ff00 >> 8;
#define ABC_P3(order) order & 0x000000ff;

#define AX_P1(order) order & 0x00ffffff;
#define AX_P2(order) 0;
#define AX_P3(order) 0;

#define RMX_P1(order) order & 0x00ff0000 >> 16;
#define RMX_P2(order) order & 0x0000ffff;
#define RMX_P3(order) 0;

#define MRX_P1(order) order & 0x00ffff00 >> 8;
#define MRX_P2(order) order & 0x000000ff;
#define MRX_P3(order) 0;

int stack_width = sizeof(uint64_t);

const int text_size = 1024;
const int stack_size = 2048;
const int int_pool_size = 1024;

//指令集
enum orders
{
    nop = 1,
    iadd,
    isub,
    imul,
    idiv,
    imod,
    igt,
    ilt,
    ieq,
    ineq,
    igteq,
    ilteq,
    iinc,
    idec,
    fadd,
    fsub,
    fmul,
    fdiv,
    FMOD,
    fgt,
    flt,
    feq,
    fneq,
    fgteq,
    flteq,
    finc,
    fdec,
    AND,
    OR,
    NOT,
    BIT_AND,
    BIT_OR,
    XOR,
    ineg,
    fneg,
    ops,
    bload,
    sload,
    iload,
    lload,
    fload,
    dload,
    call,
    rel_call,
    call_native,
    add_pc,
    jmp,
    jtrue,
    jfalse,
    bstore,
    sstore,
    istore,
    lstore,
    fstore,
    dstore,
    pstore,
    bstore_local,
    sstore_local,
    istore_local,
    lstore_local,
    fstore_local,
    dstore_local,
    pstore_local,
    vbload,
    vsload,
    viload,
    vlload,
    vfload,
    vdload,
    vpload,
    vbload_local,
    vsload_local,
    viload_local,
    vlload_local,
    vfload_local,
    vdload_local,
    vpload_local,
    pbget,
    psget,
    piget,
    plget,
    pfget,
    pdget,
    ppget,
    pbget_rel,
    psget_rel,
    piget_rel,
    plget_rel,
    pfget_rel,
    pdget_rel,
    ppget_rel,
    pbset,
    psset,
    piset,
    plset,
    pfset,
    pdset,
    ppset,
    pbset_rel,
    psset_rel,
    piset_rel,
    plset_rel,
    pfset_rel,
    plset_rel,
    ppset_rel,
    syscall,
    itof,
    ftoi,
    ensure_stack,
    ret,
    mov,
    cpush,
    spush,
    ipush,
    lpush,
    fpush,
    dpush,
    ppush,
    cpop,
    ipop,
    lpop,
    fpop,
    dpop,
    ppop
};

enum
{
    pc_i = 1,
    sp_i,
    bp_i,
    ax_i,
    flag_i
};

enum
{
    ABC = 1,
    AX,
    RMX,
    MRX
};

int opType(int code)
{
    switch (code)
    {
    case iadd:
    case idec:
        return ABC;
    case ipush:
        return AX;
    }
}

int getCode(uint64_t *order)
{
    return (*order) & 0xff000000 >> 24;
}

unsigned char *arg_r, *arg_k, *arg_n;
struct opcode
{
    unsigned char testFlag;
    unsigned char *argAMode;
    unsigned char *argBMode;
    unsigned char *argCMode;
    unsigned char *opMode;
    unsigned char code;
    string *name;
};

class CVM
{
public:
    char *path;
    int int_pool[int_pool_size];
    long long_pool[1024];
    float float_pool[1024];
    double double_pool[1024];
    char char_pool[1024];
    unsigned char byte_pool[1024];
    short short_pool[1024];
    uint64_t point_pool[1024];

    unsigned char const_byte_pool[1024];
    string const_string_pool[1024];
    long const_long_pool[1024];
    double const_double_pool[1024];
    short const_short_pool[1024];



    uint64_t *registers[16];
    //寄存器
    uint64_t *pc, *sp, *bp, *ax, *flag;
    uint64_t *text, *stack;
    char *data;
    uint64_t *text_end;

    int init()
    {
        registers[pc_i] = pc;
        registers[sp_i] = sp;
        registers[bp_i] = bp;
        registers[ax_i] = ax;
        registers[flag_i] = flag;

        text = (uint64_t *)malloc(text_size);
        stack = (uint64_t *)malloc(stack_size);

        //初始化常量池

        if (!load(path))
        {
            printf("Err: load file fail.");
        }

        pc = text;
        sp = stack;
        return 0;
    };

    int shutdown()
    {
        free(text);
        free(stack);

        return 0;
    };

    int load(char *path)
    {
        FILE *fp = fopen(path, "rb");
        if (!fp)
        {
            printf("Err:File %s not exist\n", path);
            return 0;
        }

        int cur_p = ftell(fp);
        fseek(fp, 0, SEEK_END);
        int file_size = ftell(fp);
        fseek(fp, cur_p, SEEK_SET);
        fread(text, file_size, 1, fp);
        text_end = text + file_size + 4;
        fclose(fp);
    };

    void run()
    {
        exec();
    };

    void exec();

    uint64_t *getRegister(int no)
    {
        return registers[no];
    }
};

int three_addr[];
int second_addr[];
int one_addr[];
int no_addr[];
void CVM::exec()
{
    while (pc != text_end)
    {
        uint64_t *order = pc++;

        //识别指令
        int code = getCode(order);
        int p1, p2, p3;
        switch (opType(code))
        {
        case ABC:
        {
            p1 = ABC_P1(*order);
            p2 = ABC_P2(*order);
            p3 = ABC_P3(*order);
        }
        case RMX:
        {
            p1 = RMX_P1(*order);
            p2 = RMX_P2(*order);
            p3 = RMX_P3(*order);
        }
        }

        switch (code)
        {
        case iadd:
        case fadd:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);
            *r3 = (*r2) + (*r1);
            break;
        }
        case isub:
        case fsub:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);
            *r3 = (*r2) - (*r1);
            break;
        }
        case imul:
        case fmul:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);
            *r3 = (*r2) * (*r1);
            break;
        }
        case idiv:
        case fdiv:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);
            *r3 = (*r2) / (*r1);
            break;
        }
        case imod:
        case FMOD:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);
            *r3 = (*r2) % (*r1);
            break;
        }
        case iload:
        {
            int v = int_pool[p1];
            uint64_t *r = getRegister(p2);
            if (!r)
            {
                printf("register no found.");
            }
            *r = v;
            break;
        }
        case fload:
        {
            float v = int_pool[p1];
            uint64_t *r = getRegister(p2);
            if (!r)
            {
                printf("register no found.");
            }
            *r = v;
            break;
        }
        case igt:
        case fgt:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            (*r1) > (*r2) ? *flag = 0x1 : *flag = 0x0;

            break;
        }
        case ilt:
        case flt:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            (*r1) < (*r2) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case ieq:
        case feq:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            (*r1) == (*r2) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case ineq:
        case fneq:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            (*r1) != (*r2) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case igteq:
        case fgteq:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            (*r1) >= (*r2) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case ilteq:
        case flteq:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            (*r1) <= (*r2) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case iinc:
        case finc:
        {
            uint64_t *r1 = getRegister(p1);
            (*r1)++;
            break;
        }
        case idec:
        case fdec:
        {
            uint64_t *r1 = getRegister(p1);
            (*r1)--;
            break;
        }
        case AND:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            ((*r1) && (*r2)) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case OR:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);

            ((*r1) || (*r2)) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case NOT:
        {
            uint64_t *r1 = getRegister(p1);

            !(*r1) ? *flag = 0x1 : *flag = 0x0;
            break;
        }
        case BIT_AND:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);

            *r3 = (*r1) & (*r2);
            break;
        }
        case BIT_OR:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);

            *r3 = (*r1) | (*r2);
            break;
        }
        case XOR:
        {
            uint64_t *r1 = getRegister(p1);
            uint64_t *r2 = getRegister(p2);
            uint64_t *r3 = getRegister(p3);

            *r3 = (*r1) ^ (*r2);
            break;
        }
        case ineg:
        case fneg:
        {
            uint64_t *r1 = getRegister(p1);
            (*r1) = ~(*r1) + 1;
            break;
        }
        case ops:
        {
            uint64_t *r1 = getRegister(p1);
            (*r1) = ~(*r1);
            break;
        }
        case bload:
        {
            unsigned char v = const_byte_pool[p1];
            uint64_t *r1 = getRegister(p2);
            *r1 = v;
            break;
        }
        case sload:
        {
            short v = const_short_pool[p1];
            uint64_t *r1 = getRegister(p2);
            *r1 = v;
            break;
        }
        case lload:
        {
            long v = const_long_pool[p1];
            uint64_t *r1 = getRegister(p2);
            *r1 = v;
            break;
        }
        case dload:
        {
            double v = const_double_pool[p1];
            uint64_t *r1 = getRegister(p2);
            *r1 = v;
            break;
        }
        case call:
        {
            sp = sp + sizeof(uint64_t);
            *sp = *pc;
            *pc = *pc + p1; //相对地址跳转
            break; 
        }
        case rel_call:
        {
            auto r1 = getRegister(p1);
            sp = sp + sizeof(uint64_t);
            *sp = *pc;
            *pc = *r1;
            break;
        }
        case call_native:
        {

        }
        case add_pc:
        {

            break;
        }
        case jmp:
        {
            *pc = p1;
        }
        case jtrue:
        {
            if(flag) {
                *pc = p1;
            }
            break;
        }
        case jfalse:
        {
            if(!flag) {
                *pc = p1;
            }
            break;
        }
        case bstore:
        {
            auto r1 = getRegister(p1);
            byte_pool[p2] = *r1;
            break;
        }
        case sstore:
        {
            auto r1 = getRegister(p1);
            short_pool[p2] = *r1;
            break;
        }
        case istore:
        {
            auto r1 = getRegister(p1);
            int_pool[p2] = *r1;
            break;
        }
        case lstore:
        {
            auto r1 = getRegister(p1);
            long_pool[p2] = *r1;
            break;
        }
        case fstore:
        {
            auto r1 = getRegister(p1);
            float_pool[p2] = *r1;
            break;
        }
        case dstore:
        {
            auto r1 = getRegister(p1);
            double_pool[p2] = *r1;
            break;
        }
        case pstore:
        {
            auto r1 = getRegister(p1);
            point_pool[p2] = *r1;
            break;
        }
        case bstore_local:
        case sstore_local:
        case istore_local:
        case lstore_local:
        case fstore_local:
        case dstore_local:
        case pstore_local:
        {
            auto r = getRegister(p1);

            *(sp - p2) = *r;
            break;
        }
        case vbload:
        {
            auto r = getRegister(p2);
            *r = byte_pool[p1];
            break;
        }
        case vsload:
        {
            auto r = getRegister(p2);
            *r = short_pool[p1];
            break;

        }
        case viload:
        {
            auto r = getRegister(p2);
            *r = int_pool[p1];
            break;
        }
        case vlload:
        {
            auto r = getRegister(p2);
            *r = long_pool[p1];
            break;
        }
        case vfload:
        {
            auto r = getRegister(p2);
            *r = float_pool[p1];
            break;
        }
        case vdload:
        {
            auto r = getRegister(p2);
            *r = byte_pool[p1];
            break;
        }
        case vpload:
        {
            auto r = getRegister(p2);
            *r = point_pool[p1];
            break;
        }
        case vbload_local:
        case vsload_local:
        case viload_local:
        case vlload_local:
        case vfload_local:
        case vdload_local:
        case vpload_local:
        {
            auto r = getRegister(p2);
            *r = *(sp - p1);
            break;
        }
        case pbget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(unsigned char *)(*r1);
        }
        case psget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(unsigned char *)(*r1);
        }
        case piget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(int *)(*r1);
        }
        case plget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(long *)(*r1);
        }
        case pfget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(float *)(*r1);
        }
        case pdget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = *(double *)(*r1);
        }
        case ppget:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = (uint64_t)*(void**)(*r1);
        }
        // x = y[i]
        case pbget_rel:
        case psget_rel:
        case piget_rel:
        case plget_rel:
        case pfget_rel:
        case pdget_rel:
        case ppget_rel:
        {
            auto r2 = getRegister(p3);
            *r2 = *getRegister(p1) + p2;
            break;
        }
        case pbset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(unsigned char *)*r2 = *r1;
        }
        case psset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(short *)*r2 = *r1;
        }
        case piset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(int *)*r2 = *r1;
        }
        case plset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(long *)*r2 = *r1;
        }
        case pfset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(float *)*r2 = *r1;
        }
        case pdset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(double *)*r2 = *r1;
        }
        case ppset:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *(void **)*r2 =(void *) *r1;
        }
        case pbset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(unsigned char *)(*r1 + p2) = *r2;

        }
        case psset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(short *)(*r1 + p2) = *r2;
        }
        case piset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(int *)(*r1 + p2) = *r2;
        }
        case plset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(long *)(*r1 + p2) = *r2;
        }
        case pfset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(float *)(*r1 + p2) = *r2;
        }
        case ppset_rel:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p3);
            *(void **)(*r1 + p2) = (void *)*r2;
        }
        case syscall:
        {
        }
        case itof:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = (float)*r1;
            break;
        }
        case ftoi:
        {
            auto r1 = getRegister(p1);
            auto r2 = getRegister(p2);
            *r2 = (int)*r1;
            break;
        }
        case ensure_stack:
        {
        }
        case ret:
        {
            //64位的指针长度, stack是数组，所以用减法
            *pc = *sp;
            sp = sp - sizeof(uint64_t);
        
        }
        case mov:
        {
        
        }
        case cpush:
        case spush:
        case ipush:
        case lpush:
        case fpush:
        case dpush:
        case ppush:
        {
            auto r = getRegister(p1);
            *sp = *r;
            sp += sizeof(uint64_t);
            break;
        }
        case ipop:
        case lpop:
        case fpop:
        case dpop:
        case ppop:
        case cpop:
        {
            auto r = getRegister(p1);
            *r = *sp;
            sp -= sizeof(uint64_t);
            break; 
        }
        default:
            break;
        }
    }
}

//main
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("Err:No file input\n");
        return -1;
    }
    
}